package com.softserveinc.ita.kaiji.multiplay.domain.player;


import com.softserveinc.ita.kaiji.multiplay.domain.InvalidActionException;
import com.softserveinc.ita.kaiji.multiplay.domain.card.Card;
import com.softserveinc.ita.kaiji.multiplay.domain.card.Deck;
import com.softserveinc.ita.kaiji.multiplay.domain.message.InOutMessage;
import com.softserveinc.ita.kaiji.multiplay.domain.room.Ship;
import com.softserveinc.ita.kaiji.multiplay.domain.table.ShipTable;
import com.softserveinc.ita.kaiji.multiplay.dto.server.DtoMessage;
import com.softserveinc.ita.kaiji.multiplay.dto.server.player.DtoShipPlayer;
import com.softserveinc.ita.kaiji.multiplay.dto.server.player.DtoShipPlayerPublic;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Project kaiji-game
 * Created by mikeldpl
 * 25.03.2015 4:18.
 */
public class ShipPlayer extends BasePlayer {
	private final Deck deck;
	private int numberOfStart;
	private Set<ShipPlayer> iChallengedPlayers = new HashSet<>();

	public ShipPlayer(String name, Ship ship, int numberOfStart,
					  int numberOfCardsOneTypeConfig, int numberOfCardsTypesConfig) {
		super(name, ship);
		setNumberOfStart(numberOfStart);
		deck = new Deck(numberOfCardsOneTypeConfig, numberOfCardsTypesConfig);
	}

	public Deck getDeck() {
		return deck;
	}
//--------------------------------------------------------------------------------stars

	public int getNumberOfStart() {
		return numberOfStart;
	}

	private void setNumberOfStart(int numberOfStart) {
		if (numberOfStart < 0) {
			throw new InvalidDomainPlayerStateException("Number of stars can't be less then 0");
		}
		this.numberOfStart = numberOfStart;
	}

	public void setNumberOfStart(InOutMessage msg, int numberOfStart) {
		setNumberOfStart(numberOfStart);
		msg.addDomainThatMustBeSanded(getPublisher());
		if (numberOfStart == 0) {
			setState(msg, StateOfPlayer.LOSS);
		}
	}

	public void setState(InOutMessage msg, StateOfPlayer state) {
		super.setState(msg, state);
		if (this.getState() == StateOfPlayer.LOSS) {
			dropCards(msg);
			Iterator<ShipPlayer> iterator = (Iterator) getPublisher().getPlayers().getPlayers().iterator();
			while (iterator.hasNext()) {
				ShipPlayer player = iterator.next();
				if (isChallengedPlayer(player))
					removeChallengedPlayer(msg, player);
				if (player.isChallengedPlayer(this))
					player.removeChallengedPlayer(msg, this);
			}
		}
	}

//--------------------------------------------------------------------------------core

	@Override
	public void processMessage(InOutMessage message) {
		ShipPlayer playerByName = (ShipPlayer) getPublisher().getPlayers().getPlayerByName(message.getIn().getName());
		if (playerByName == null) {
			throw new InvalidActionException(message.getIn().getName() + " can't find player");
		}
		if (this.getState() != StateOfPlayer.ONLINE || playerByName.getState() != StateOfPlayer.ONLINE) {
			throw new InvalidActionException("Invalid player State");
		}
		if (playerByName.isChallengedPlayer(this)) {
			ShipTable freeTable = getPublisher().getTables().getFreeTable();
			if (freeTable != null) {
				freeTable.addSubscriber(this.getInfo());
				freeTable.addSubscriber(playerByName.getInfo());
				freeTable.sitOn(message, this, playerByName);
			} else {
				sendMessageToSubscribers(new DtoMessage().setMsg("All Tables isn't free! Wait please."));
			}
		} else {
			addChallengedPlayer(message, playerByName);
		}
	}

//--------------------------------------------------------------------------------Challenges

	public void addChallengedPlayer(InOutMessage msg, ShipPlayer player) {
		if (getState() == StateOfPlayer.ONLINE && player.getState() == StateOfPlayer.ONLINE
				&& !this.equals(player)) {
			iChallengedPlayers.add(player);
			msg.addDomainThatMustBeSanded(this);
			msg.addDomainThatMustBeSanded(player);
		} else {
			throw new InvalidActionException("Can't challenge " + this + " -> " + player);
		}
	}

	public void removeChallengedPlayer(InOutMessage msg, ShipPlayer player) {
		if (iChallengedPlayers.contains(player)) {
			iChallengedPlayers.remove(player);
			msg.addDomainThatMustBeSanded(this);
			msg.addDomainThatMustBeSanded(player);
		} else {
			throw new InvalidActionException("Can't remove challenge " + this + " -> " + player);
		}
	}

	public boolean isChallengedPlayer(ShipPlayer player) {
		return iChallengedPlayers.contains(player);
	}

	//--------------------------------------------------------------------------------cards
	public void decreaseCard(InOutMessage msg, Card card) {
		System.out.println(iChallengedPlayers);
		deck.decreaseCard(card);
		getRoom().decreaseCard(msg, card);
		msg.addDomainThatMustBeSanded(this);
		if (!deck.isNotEmpty()) {
			this.setState(msg, StateOfPlayer.WIN);
		}
	}

	public void dropCards(InOutMessage msg) {
		for (Map.Entry<Card, Integer> cardIntegerEntry : getDeck().getDeck().entrySet()) {
			Card card = cardIntegerEntry.getKey();
			getRoom().setNumberCard(msg, card, getRoom().getDeck().getNumberOfCards(card)
					- cardIntegerEntry.getValue());
			getDeck().setNumberOfCards(card, 0);
			msg.addDomainThatMustBeSanded(this);
		}
	}

	//--------------------------------------------------------------------------------
	@Override
	public DtoShipPlayer getDto() {
		return new DtoShipPlayer(getName());
	}

	@Override
	public DtoShipPlayer getEntireDto() {
		DtoShipPlayer entireDto = (DtoShipPlayer) super.getEntireDto();
		entireDto.getDeck().putAll(deck.getMapStToInt());
		for (ShipPlayer iChallengedPlayer : iChallengedPlayers) {
			entireDto.addIChallenged(iChallengedPlayer.getPublicDto());
		}
		Iterator<ShipPlayer> iterator = (Iterator) getPublisher().getPlayers().getPlayers().iterator();
		while (iterator.hasNext()) {
			ShipPlayer player = iterator.next();
			if (player.isChallengedPlayer(this)) {
				entireDto.addWhoChallenging(player.getPublicDto());
			}
		}
		return entireDto;
	}

	@Override
	public Ship getPublisher() {
		return (Ship) super.getPublisher();
	}

	@Override
	public DtoShipPlayerPublic getPublicDto() {
		return new DtoShipPlayerPublic(getName());
	}

	@Override
	public DtoShipPlayerPublic getEntirePublicDto() {
		return ((DtoShipPlayerPublic) super.getEntirePublicDto()).setStars(getNumberOfStart());
	}


	@Override
	public Ship getRoom() {
		return (Ship) super.getRoom();
	}
}
